#include "common.h"
#include "utils/systime.h"

#include "Vbasediv.h"

#include "Vbasediv___024root.h"
#include "verilated_vcd_c.h"
#define TRACE 0
#define TEST_CNT 100000
#define WAVE_MAX_CNT 1000
#define SIM_MAX_CYCLES (TEST_CNT * 100)
uint64_t total_cycles = 0;
Vbasediv *top;
VerilatedVcdC *tfp;

void init_trace(const char *filename)
{
#if TRACE
    tfp = new VerilatedVcdC; // 初始化VCD对象指针
    Verilated::traceEverOn(true); // 打开追踪功能
    top->trace(tfp, 0);
    tfp->open(filename); // 打开vcd
#endif
}
void quit(int ret)
{
#if TRACE
    tfp->close();
    delete tfp;
#endif
    delete top;
    exit(ret);
}
void dump_wave()
{
#if TRACE
    static uint32_t wave_cnt = 0;
    if (wave_cnt > WAVE_MAX_CNT)
        return;
    tfp->dump(wave_cnt++);
#endif
}

void simcycle(uint32_t n)
{
    total_cycles += n;
    while (n--)
    {
        top->clk = 0;
        top->eval();
        dump_wave();
        top->clk = 1;
        top->eval();
        dump_wave();
    }
    if (total_cycles > SIM_MAX_CYCLES)
        quit(-1);
}
void reset()
{
    top->clk = 0;
    top->rst = 1;
    top->flush = 0;
    top->eval();
    simcycle(10);
    top->rst = 0;
}
bool div_test(uint64_t src1, uint64_t src2, uint8_t sign, uint8_t w)
{
    top->valid_i = 0;
    while (top->ready == 0)
        simcycle(1);
    total_cycles = 0;
    top->src1 = src1;
    top->src2 = src2;
    top->div_signed = sign;
    top->divw = w;
    top->valid_i = 1;

    uint64_t rem,quo,x,y;
    uint8_t x_sign,y_sign;
    if(w) {
        x_sign = sign&&BITS(src1,31,31);
        if(x_sign) x = abs((int64_t)SEXT(src1,32));
        else x = SEXT(src1,32);
        y_sign = sign&&BITS(src2,31,31);
        if(y_sign) y = abs((int64_t)SEXT(src2,32));
        else y = SEXT(src2,32);
    } else {
        x_sign = sign&&BITS(src1,63,63);
        if(x_sign) x = abs((int64_t)SEXT(src1,64));
        else x = SEXT(src1,64);
        y_sign = sign&&BITS(src2,63,63);
        if(y_sign) y = abs((int64_t)SEXT(src2,64));
        else y = SEXT(src2,64);
    }
    rem = x%y;
    quo = x/y;
    if(x_sign) rem = -rem;
    if(x_sign^y_sign) quo = -quo;
    simcycle(1);
    top->valid_i = 0;
    while (top->valid_o == 0)
        simcycle(1);
    // printf("total_cycles:%ld\n",total_cycles);
    if (rem != top->remainder || quo != top->quotient)
    {
        printf("error occur: src1:%lu src2:%lu sign:0x%x w:%d\n", src1, src2, sign, w);
        printf("\tref: rem: 0x%016lx quo: 0x%016lx x:0x%016lx y:0x%016lx\n", rem, quo,x,y);
        printf("\tdut: ");
        if (rem != top->remainder)
            printf(ANSI_FMT("rem: 0x%016lx ", ANSI_FG_RED), top->remainder);
        else
            printf("rem: 0x%016lx ", top->remainder);
        if (quo != top->quotient)
            printf(ANSI_FMT("quo: 0x%016lx", ANSI_FG_RED), top->quotient);
        else
            printf("quo: 0x%016lx", top->quotient);
        printf("\n");
        return false;
    }
    return true;
}
int main(int argc, char **argv, char **env)
{
    top = new Vbasediv;
    init_trace("wave_sim_basediv.vcd");
    srand(0);
    reset();
    printf("div test start\n");
    uint64_t start_us, end_us;
    start_us = SYS_GetTicks();
    uint32_t test_cnt = 0;
    for (int i = 0; i < TEST_CNT; i++)
    {
        test_cnt++;
        int src[4];
        for (int j = 0; j < 4; j++)
        {
            src[j] = rand();
        }
        uint64_t src1 = *(uint64_t *)(&src[0]);
        uint64_t src2 = *(uint64_t *)(&src[2]);
        if (div_test(src1, src2, rand() % 2, rand() % 2) == false)
        {
            break;
        }
    }
    end_us = SYS_GetTicks();
    printf("div test end\n");

    printf("test cnt:%u cost time : %.03lfms\n", test_cnt, (end_us - start_us) / 1000.0);
    printf("test speed is %d mul/ms\n", (int)(test_cnt / ((end_us - start_us) / 1000.0)));
#if TRACE
    tfp->close();
    delete tfp;
#endif
    delete top;
    return 0;
}

#ifdef __cplusplus
extern "C"
{
#endif

    // DPI IMPORTS
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Cache/axi_mem.v:1:33
    long long mem_read(long long raddr, svBit is_icache) { return 0; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Cache/axi_mem.v:2:30
    void mem_write(long long waddr, long long wdata, char wmask) { return; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/Execute/Execute.v:198:32
    void other_inst(svBit ecall, svBit fence, svBit ebreak) { return; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/sim/top.v:1:33
    long long pmem_read(long long raddr, char rsize, char is_icache) { return 0; }
    // DPI import at /home/qingchen/Project/ysyx-workbench/npc/vsrc/sim/top.v:2:30
    void pmem_write(long long waddr, long long wdata, char wmask) { return; }
    void raise_hard_intr() {}
    void submit_inst(long long pc, int inst, long long next_pc) {}
    void difftest_memory_access(long long addr) {}

#ifdef __cplusplus
}
#endif
